package net.risesoft.api.permission;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.context.annotation.Primary;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;

import net.risesoft.entity.identity.Y9IdentityToResourceAndAuthorityBase;
import net.risesoft.entity.identity.person.Y9PersonToResourceAndAuthority;
import net.risesoft.enums.AuthorityEnum;
import net.risesoft.enums.ResourceTypeEnum;
import net.risesoft.model.Menu;
import net.risesoft.model.Resource;
import net.risesoft.model.VueButton;
import net.risesoft.model.VueMenu;
import net.risesoft.service.identity.Y9PersonToResourceAndAuthorityService;
import net.risesoft.y9.Y9LoginUserHolder;
import net.risesoft.y9.util.Y9ModelConvertUtil;
import net.risesoft.y9public.entity.resource.Y9Menu;
import net.risesoft.y9public.entity.resource.Y9Operation;
import net.risesoft.y9public.entity.resource.Y9ResourceBase;
import net.risesoft.y9public.manager.Y9ResourceBaseManager;
import net.risesoft.y9public.service.resource.Y9MenuService;
import net.risesoft.y9public.service.resource.Y9OperationService;

/**
 * 人员资源权限查看组件
 *
 * @author dingzhaojun
 * @author qinman
 * @author mengjuhua
 * @date 2022/2/10
 * @since 9.6.0
 */
@Primary
@RestController
@RequestMapping(value = "/services/rest/personResource", produces = MediaType.APPLICATION_JSON_VALUE)
@RequiredArgsConstructor
public class PersonResourceApiImpl implements PersonResourceApi {

    private final Y9ResourceBaseManager y9ResourceBaseManager;
    private final Y9MenuService y9MenuService;
    private final Y9OperationService y9OperationService;
    private final Y9PersonToResourceAndAuthorityService y9PersonToResourceAndAuthorityService;

    private void getMenusRecursively(@RequestParam String personId, @RequestParam Integer authority, @RequestParam String resourceId, @RequestParam List<VueMenu> vueMenuList) {
        List<Y9PersonToResourceAndAuthority> authorizedMenuList = y9PersonToResourceAndAuthorityService.list(personId, resourceId, ResourceTypeEnum.MENU.getValue(), authority, true);
        List<String> menuIdList = authorizedMenuList.stream().map(Y9IdentityToResourceAndAuthorityBase::getResourceId).collect(Collectors.toList());
        for (String menuId : menuIdList) {
            Y9Menu y9Menu = y9MenuService.getById(menuId);
            VueMenu vueMenu = new VueMenu();
            vueMenu.setName(y9Menu.getName());
            vueMenu.setPath(y9Menu.getUrl());
            vueMenu.setRedirect(y9Menu.getRedirect());
            vueMenu.setComponent(y9Menu.getComponent());
            vueMenu.setMeta(y9Menu.getMeta());
            vueMenu.setTarget(y9Menu.getTarget());

            List<VueMenu> subVueMenuList = new ArrayList<>();
            getMenusRecursively(personId, authority, menuId, subVueMenuList);
            vueMenu.setChildren(subVueMenuList);

            List<VueButton> buttonList = new ArrayList<>();
            List<Y9PersonToResourceAndAuthority> authorizedButtonList = y9PersonToResourceAndAuthorityService.list(personId, menuId, ResourceTypeEnum.OPERATION.getValue(), authority, true);
            List<String> buttonIdList = authorizedButtonList.stream().map(Y9IdentityToResourceAndAuthorityBase::getResourceId).collect(Collectors.toList());
            for (String buttonId : buttonIdList) {
                Y9Operation y9Operation = y9OperationService.getById(buttonId);
                VueButton button = new VueButton();
                button.setName(y9Operation.getName());
                button.setIcon(y9Operation.getIconUrl());
                button.setButtonId(y9Operation.getCustomId());
                button.setDisplayType(y9Operation.getDisplayType());
                button.setUrl(y9Operation.getUrl());
                button.setEventName(y9Operation.getEventName());
                buttonList.add(button);
            }
            vueMenu.setButtons(buttonList);

            vueMenuList.add(vueMenu);
        }
    }

    /**
     * 判断person对resource是否有指定的操作权限
     *
     * @param tenantId 租户id
     * @param personId 操作者唯一标识
     * @param resourceId 资源唯一标识
     * @param authority 操作类型 {@link AuthorityEnum}
     * @return Boolean 是否有权限
     * @since 9.6.0
     */
    @Override
    @GetMapping("/hasPermission")
    public boolean hasPermission(@RequestParam String tenantId, @RequestParam String personId, @RequestParam String resourceId, @RequestParam Integer authority) {
        Y9LoginUserHolder.setTenantId(tenantId);
        return y9PersonToResourceAndAuthorityService.hasPermission(personId, resourceId, authority);
    }

    /**
     * 判断 person 对 customId 对应的 resource 是否有指定的操作权限
     *
     * @param tenantId 租户id
     * @param personId 人员id
     * @param customId 自定义id
     * @param authority 操作类型 {@link AuthorityEnum}
     * @return boolean
     * @since 9.6.0
     */
    @Override
    @GetMapping("/hasPermissionByCustomId")
    public boolean hasPermissionByCustomId(@RequestParam String tenantId, @RequestParam String personId, @RequestParam String customId, @RequestParam Integer authority) {
        Y9LoginUserHolder.setTenantId(tenantId);
        return y9PersonToResourceAndAuthorityService.hasPermissionByCustomId(personId, customId, authority);
    }

    /**
     * 递归获得某一资源下,主体对象有相应权限的菜单
     *
     * @param tenantId 租户id
     * @param personId 操作者唯一标识
     * @param authority 操作类型 {@link AuthorityEnum}
     * @param resourceId 资源唯一标识
     * @return List&lt;Resource&gt; 有权限的子菜单
     * @since 9.6.0
     */
    @Override
    @GetMapping("/listMenusRecursively")
    public List<VueMenu> listMenusRecursively(@RequestParam String tenantId, @RequestParam String personId, @RequestParam Integer authority, @RequestParam String resourceId) {
        Y9LoginUserHolder.setTenantId(tenantId);
        List<VueMenu> vueMenuList = new ArrayList<>();

        getMenusRecursively(personId, authority, resourceId, vueMenuList);

        return vueMenuList;
    }

    /**
     * 获得某一资源下,主体对象有相应操作权限的子菜单
     *
     * @param tenantId 租户id
     * @param personId 操作者唯一标识
     * @param authority 操作类型 {@link AuthorityEnum}
     * @param resourceId 资源唯一标识
     * @return List&lt;Resource&gt; 有操作权限的子菜单
     * @since 9.6.0
     */
    @Override
    @GetMapping("/listSubMenus")
    public List<Menu> listSubMenus(@RequestParam String tenantId, @RequestParam String personId, @RequestParam Integer authority, @RequestParam String resourceId) {
        Y9LoginUserHolder.setTenantId(tenantId);

        List<Y9PersonToResourceAndAuthority> y9PersonToResourceAndAuthorityList = y9PersonToResourceAndAuthorityService.list(personId, resourceId, ResourceTypeEnum.MENU.getValue(), authority, true);
        List<String> menuIdList = y9PersonToResourceAndAuthorityList.stream().map(Y9IdentityToResourceAndAuthorityBase::getResourceId).collect(Collectors.toList());
        List<Menu> resourceList = new ArrayList<>();
        for (String menuId : menuIdList) {
            Y9Menu y9Menu = y9MenuService.getById(menuId);
            if (y9Menu != null) {
                resourceList.add(Y9ModelConvertUtil.convert(y9Menu, Menu.class));
            }
        }
        return resourceList;
    }

    /**
     * 获得某一资源下,主体对象有相应操作权限的子节点
     *
     * @param tenantId 租户id
     * @param personId 操作者唯一标识
     * @param authority 操作类型 {@link AuthorityEnum}
     * @param resourceId 资源唯一标识
     * @return List&lt;Resource&gt; 有操作权限的子节点
     * @since 9.6.0
     */
    @Override
    @GetMapping("/listSubResources")
    public List<Resource> listSubResources(@RequestParam String tenantId, @RequestParam String personId, @RequestParam Integer authority, @RequestParam String resourceId) {
        Y9LoginUserHolder.setTenantId(tenantId);
        Set<Resource> resourceList = new HashSet<>();
        List<Y9PersonToResourceAndAuthority> y9PersonToResourceAndAuthorityList = y9PersonToResourceAndAuthorityService.list(personId, resourceId, authority);
        for (Y9PersonToResourceAndAuthority personResource : y9PersonToResourceAndAuthorityList) {
            Y9ResourceBase y9ResourceBase = y9ResourceBaseManager.findByIdAndResourceType(personResource.getResourceId(), personResource.getResourceType());
            if (y9ResourceBase != null) {
                resourceList.add(Y9ModelConvertUtil.convert(y9ResourceBase, Resource.class));
            }
        }
        List<Resource> returnResourceList = new ArrayList<>();
        returnResourceList.addAll(resourceList);
        return returnResourceList;
    }
}
